{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "e5d6c155",
   "metadata": {},
   "source": [
    "# 第二章：深度学习通用流程之图像类数据预处理代码2-4\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "46ccf339",
   "metadata": {},
   "source": [
    "## 0 导入包和设置中文路径可读取阶段"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "5bccd13d",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "e:\\Anaconda3\\envs\\py38_pytorch_cpu\\lib\\site-packages\\tqdm\\auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n",
      "  from .autonotebook import tqdm as notebook_tqdm\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "'zh_CN.UTF-8'"
      ]
     },
     "execution_count": 1,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 使用ipynb文件前，pip install ipykernel\n",
    "# 图片数据预处理\n",
    "import os # 导入os库用于设置环境变量\n",
    "import locale # 用于设置系统区域环境\n",
    "import torch # 导入PyTorch库\n",
    "from torchvision import transforms#  # 导入torchvision中的预训练模型\n",
    "import certifi#  # 用于解决SSL证书问题\n",
    "from PIL import Image # 导入PIL库用于图像处理\n",
    "# 实验数据目录在../Pytorch与深度学习实战/code/第二章/data/kaggle_cat_or_dog。\n",
    "\n",
    "# 设置系统区域环境\n",
    "os.environ['PYTHONIOENCODING'] = 'utf-8'\n",
    "os.environ['LC_ALL'] = 'zh_CN.UTF-8' # 设置中文环境，以便解决中文路径问题。\n",
    "os.environ['SSL_CERT_FILE'] = certifi.where()# 解决SSL证书问题\n",
    "# 应用设置\n",
    "locale.setlocale(locale.LC_ALL, 'zh_CN.UTF-8')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "065a46a4",
   "metadata": {},
   "source": [
    "## 1 读取图片并编写存储函数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "4a94f3dc",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "图片尺寸为：(300, 280)\n",
      "图片格式为：None\n",
      "图片模式为：RGB\n",
      "图片数据类型为：<class 'PIL.Image.Image'>\n"
     ]
    }
   ],
   "source": [
    "def read_image(file_path):\n",
    "    image = Image.open(file_path).convert(\"RGB\") # 读取图片\n",
    "    image.show() # 显示图片\n",
    "    print(f\"图片尺寸为：{image.size}\") # 输出图片尺寸\n",
    "    print(f\"图片格式为：{image.format}\") # 输出图片格式\n",
    "    print(f\"图片模式为：{image.mode}\") # 输出图片模式\n",
    "    print(f\"图片数据类型为：{type(image)}\") # 输出图片数据类型\n",
    "    return image\n",
    "\n",
    "def save_image(image,save_path):\n",
    "    image.save(save_path)#保存图片\n",
    "    print(f\"保存图片至：{save_path}\")\n",
    "    \n",
    "file_path = '../data/kaggle_cat_or_dog/train/cat.1.jpg' # 图片路径\n",
    "image = read_image(file_path) # 读取图片\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9d392ea4",
   "metadata": {},
   "source": [
    "## 2 torchvision.transforms.CenterCrop\n",
    "* **功能：** \n",
    "用于对图片进行中心裁剪\n",
    "* **参数：** \n",
    "  \n",
    "| 参数名 | 类型 | 必需 | 默认值 | 描述 |\n",
    "|--------|------|------|---------|------|\n",
    "| `size` | int 或 sequence | 是 | 无 | 期望的输出尺寸。<ul><li>如果为 <strong>int</strong>，则裁剪为正方形 <code>(size, size)</code>。</li><li>如果为 <strong>sequence</strong>（如列表或元组），则需提供 <code>(height, width)</code>，裁剪为该尺寸的矩形。</li></ul> |\n",
    "\n",
    "* **返回：**\n",
    "经中心裁剪后的图像，类型与输入相同（PIL Image 或 Tensor）。\n",
    "输出图像尺寸为指定的 size。\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "cec6458b",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "剪裁后的图片尺寸为：(100, 100)\n",
      "保存图片至：../tmp/cat.1_center_crop.jpg\n"
     ]
    }
   ],
   "source": [
    "def center_crop(image,size=(100,100)): # 可以随意调整这个参数感受一下\n",
    "\n",
    "    center_crop =  transforms.CenterCrop(size)\n",
    "    image_after_crop = center_crop(image)\n",
    "    image_after_crop.show() # 显示裁剪后的图片\n",
    "    print(f\"剪裁后的图片尺寸为：{image_after_crop.size}\") # 输出裁剪后的图片尺寸\n",
    "    return image_after_crop\n",
    "\n",
    "image_after_crop = center_crop(image) # 中心裁剪图片    \n",
    "save_image(image_after_crop,'../tmp/cat.1_center_crop.jpg')# 保存裁剪后的图片"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4cb0b980",
   "metadata": {},
   "source": [
    "## 3  torchvision.transforms.ColorJitter \n",
    "\n",
    "**功能：**\n",
    "通过随机扰动一张输入图像的四个关键颜色属性，来生成一张颜色上略有不同的“新”图像。\n",
    "\n",
    "**参数：**\n",
    "| 参数名 | 类型 | 默认值 | 描述 | 取值范围或格式 |\n",
    "| :--- | :--- | :--- | :--- | :--- |\n",
    "| **`brightness`** | `float` 或 `tuple` | `0` | **亮度抖动强度**。 | 非负数。若为 float，则从 `[max(0, 1 - brightness), 1 + brightness]` 均匀采样；若为 tuple (a, b)，则从 `[a, b]` 采样。 |\n",
    "| **`contrast`** | `float` 或 `tuple` | `0` | **对比度抖动强度**。 | 非负数。若为 float，则从 `[max(0, 1 - contrast), 1 + contrast]` 均匀采样；若为 tuple (a, b)，则从 `[a, b]` 采样。 |\n",
    "| **`saturation`** | `float` 或 `tuple` | `0` | **饱和度抖动强度**。 | 非负数。若为 float，则从 `[max(0, 1 - saturation), 1 + saturation]` 均匀采样；若为 tuple (a, b)，则从 `[a, b]` 采样。 |\n",
    "| **`hue`** | `float` 或 `tuple` | `0` | **色调抖动强度**。 | 值需满足 `0 <= hue <= 0.5` 或 `-0.5 <= min <= max <= 0.5`。若为 float，则从 `[-hue, hue]` 均匀采样；若为 tuple (a, b)，则从 `[a, b]` 采样。 |\n",
    "\n",
    "\n",
    "**返回：**\n",
    "与输入类型相同的颜色抖动后的新图像（PIL.Image 或 torch.Tensor）。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "085ae579",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "保存图片至：../tmp/cat.1_colour_jitter.jpg\n"
     ]
    }
   ],
   "source": [
    "def colour_jitter(image,brightness=0.1,contrast=0.5,saturation=0.5,hue=0.5):# 可以随意调整这些参数感受一下\n",
    "    colour_jitter = transforms.ColorJitter(brightness=brightness,contrast=contrast,saturation=saturation,hue=hue)\n",
    "    image_after_jitter =colour_jitter(image)\n",
    "    image_after_jitter.show() # 显示颜色抖动后的图片\n",
    "    return image_after_jitter\n",
    "\n",
    "image_after_jitter =colour_jitter(image) # 颜色抖动\n",
    "save_image(image_after_jitter,'../tmp/cat.1_colour_jitter.jpg')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3693fe12",
   "metadata": {},
   "source": [
    "## 4 torchvision.transforms.FiveCrop\n",
    "**功能：** FiveCrop 是 PyTorch torchvision.transforms 模块中的一个类，其核心功能是从一张输入图像的四个角以及正中心，裁剪出五个指定大小的区域。其主要价值体现在模型测试（预测）阶段，作为一种测试时增强（Test-Time Augmentation, TTA） 的技巧。\n",
    "\n",
    "**参数：**\n",
    "| 参数名 | 类型 | 必需 | 默认值 | 描述 |\n",
    "| :--- | :--- | :--- | :--- | :--- |\n",
    "| **`size`** | `int` 或 `sequence` | 是 | 无 | **期望的裁剪输出尺寸**。<br> - 如果为 **int**，则所有裁剪区域均为正方形 `(size, size)`。<br> - 如果为 **sequence**（如列表或元组 `(h, w)`），则裁剪为高度 `h`、宽度 `w` 的矩形。 |\n",
    "\n",
    "**返回：**\n",
    "返回一个包含5个元素的元组（Tuple），元组中的每个元素都是一张裁剪后的图像。\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cae59593",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "五点裁剪后的图片尺寸为：(100, 100)\n",
      "五点裁剪后的image_after_five_crop类型为:<class 'tuple'>\n"
     ]
    }
   ],
   "source": [
    "def five_crop(image,size=(100,100)):# 可以随意调整这个参数感受一下\n",
    "    five_crop =transforms.FiveCrop(size)\n",
    "    image_after_five_crop =five_crop(image)\n",
    "    print(f\"五点裁剪后的图片尺寸为：{image_after_five_crop[0].size}\")\n",
    "    print(f\"五点裁剪后的image_after_five_crop类型为:{type(image_after_five_crop)}\")# 五点裁剪的数据类型\n",
    "    for i,img in enumerate(image_after_five_crop):\n",
    "        img.show()\n",
    "        save_image(img,f'../tmp/cat.1_five_crop_{i}.jpg')# 保存裁剪后的图片\n",
    "    return image_after_five_crop\n",
    "image_after_five_crop = five_crop(image)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d52f66ee",
   "metadata": {},
   "source": [
    "## 5 torchvision.transforms.Grayscale\n",
    "**功能：** Grayscale 是 PyTorch torchvision.transforms 中的类，核心功能是将彩色图像转为灰度图。在深度学习中，可减少数据维度（3 通道变 1 或 3 通道）以降低计算复杂度，对字符识别等不依赖颜色的任务，能保留关键特征，提升模型训练效率与泛化能力，是常用预处理步骤。\n",
    "\n",
    "**参数：**\n",
    "| 参数名               | 类型    | 必需 | 默认值 | 描述                                                         |\n",
    "| :------------------- | :------ | :--- | :----- | :----------------------------------------------------------- |\n",
    "| **`num_output_channels`** | `int`   | 是   | 1      | 输出图像的通道数。<br>- 仅允许取值为 1 或 3。<br>- 若为 1，输出为单通道灰度图像；<br>- 若为 3，输出为三通道灰度图像（三个通道的像素值相同，模拟彩色图像格式以适配部分模型输入要求）。 |\n",
    "\n",
    "**返回：**\n",
    "\n",
    "返回转换后的灰度图像。若输入为 torch 张量且形状为 […, 3, H, W]，则输出张量形状为 […, num_output_channels, H, W]（其中 “…” 表示与输入相同的前导维数）。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "910d6db4",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "保存图片至：../tmp/cat.1_gray_scale.jpg\n"
     ]
    }
   ],
   "source": [
    "def gray_scale(image):\n",
    "    gray_scale = transforms.Grayscale(num_output_channels=3)#转为三通道灰度图\n",
    "    image_after_gray_scale = gray_scale(image)\n",
    "    image_after_gray_scale.show() # 显示灰度化后的图片\n",
    "    return image_after_gray_scale\n",
    "image_after_gray_scale =gray_scale(image)\n",
    "save_image(image_after_gray_scale,'../tmp/cat.1_gray_scale.jpg')# 保存灰度化后的图片"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "379ab24f",
   "metadata": {},
   "source": [
    "## 6 torchvision.transforms.Pad\n",
    "\n",
    "**功能：**\n",
    "\n",
    "Pad 的主要功能是对图像的边缘进行填充，以扩大图像的尺寸或保持图像操作（如旋转、裁剪等）后尺寸的一致性1。\n",
    "* 应对尺寸变化：卷积神经网络（CNN）通常要求输入图像具有固定的尺寸。当原始图像尺寸不一时，填充可以帮助将图像调整到目标尺寸。\n",
    "* 数据增强：填充是数据增强策略的一部分。例如，在进行随机旋转等变换时，填充可以防止图像角落的信息丢失，并确保输出尺寸不变。\n",
    "* 保持空间信息：填充（如反射填充）有助于在卷积操作中更好地保留图像边界的信息。\n",
    "\n",
    "**参数：**\n",
    "| 参数名 | 类型 | 必需 | 默认值 | 描述 |\n",
    "| :--- | :--- | :--- | :--- | :--- |\n",
    "| **`padding`** | int 或 sequence | 是 | 无 | 填充大小。<br>- 若为 int：图像上下左右四边均填充该值个像素。<br>- 若为长度为 2 的序列 (a, b)：左右填充 a 个像素，上下填充 b 个像素。<br>- 若为长度为 4 的序列 (a, b, c, d)：分别表示左、上、右、下的填充宽度。 |\n",
    "| **`fill`** | int 或 tuple | 否 | 0 | 填充的像素值 (仅 padding_mode 为 'constant' 时有效)。<br>- 若为 int：用于灰度图或所有通道（如果是RGB图）。<br>- 若为长度为 3 的 tuple：分别用于填充 R, G, B 通道。 |\n",
    "| **`padding_mode`** | str | 否 | 'constant' | 填充模式：<br>- 'constant'：使用常数填充<br>- 'edge'：使用图像边缘像素值填充<br>- 'reflect'：使用图像的镜像反射填充<br>- 'symmetric'：使用图像的镜像对称填充 |\n",
    "\n",
    "**返回：**\n",
    "Pad 的返回值是填充后的图像。返回的图像类型与输入类型相同：\n",
    "输入是 PIL Image，返回 PIL Image。\n",
    "输入是 torch Tensor，返回 torch Tensor。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "40438449",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "填充后的图片尺寸为：(400, 380)\n",
      "保存图片至：../tmp/cat.1_pad.jpg\n"
     ]
    }
   ],
   "source": [
    "def pad(image,padding=50,fill=0):# 可以随意调整这些参数感受一下\n",
    "    pad = transforms.Pad(padding=padding,fill=fill)\n",
    "    image_after_pad = pad(image)\n",
    "    image_after_pad.show() # 显示填充后的图片\n",
    "    print(f\"填充后的图片尺寸为：{image_after_pad.size}\") # 输出填充后的图片尺寸\n",
    "    return image_after_pad\n",
    "image_after_pad = pad(image)\n",
    "save_image(image_after_pad,'../tmp/cat.1_pad.jpg')# 保存填充后的图片"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "46650f42",
   "metadata": {},
   "source": [
    "## 7 torchvision.transforms.Resize\n",
    "**功能：**\n",
    "Resize 是 PyTorch torchvision.transforms 中的类，核心功能是将输入图像调整到指定尺寸。在深度学习中，这是最基本且必需的预处理步骤，因为卷积神经网络（CNN）通常要求输入具有固定的空间维度。\n",
    "统一输入尺寸：将不同大小的训练样本调整为相同尺寸，满足网络输入要求，便于批量处理。\n",
    "数据预处理：在下采样（缩小）或上采样（放大）图像的同时，尽可能保留重要特征，为后续的数据增强（如裁剪、旋转等）和模型训练做准备。\n",
    "控制计算复杂度：通过调整图像大小，可以显著影响模型训练和推理的计算量及内存占用\n",
    "\n",
    "**参数：**\n",
    "| 参数名 | 类型 | 必需 | 默认值 | 描述 |\n",
    "| :--- | :--- | :--- | :--- | :--- |\n",
    "| **`size`** | int 或 sequence | 是 | 无 | 期望的输出尺寸。<br>- 若为 int：图像的较短边将被缩放至此长度，较长边按比例缩放（保持原始宽高比）。<br>- 若为 sequence (h, w)：将图像精确缩放至高度 h、宽度 w 的尺寸（可能会破坏原始宽高比）。 |\n",
    "| **`interpolation`** | InterpolationMode | 否 | `InterpolationMode.BILINEAR` | 插值算法，用于确定如何计算新像素点的值。常用选项有：<br>- `InterpolationMode.BILINEAR`：双线性插值（通常为默认值，在速度和质量间取得良好平衡）。<br>- `InterpolationMode.NEAREST`：最近邻插值（最快，但可能导致锯齿状边缘）。<br>- `InterpolationMode.BICUBIC`：双三次插值（通常更慢但更平滑，可能产生伪影）。 |\n",
    "\n",
    "**返回：**\n",
    "输入是 PIL Image，返回 PIL Image。\n",
    "输入是 torch Tensor（形状为 [..., H, W, C]），则输出张量形状为 [..., size[0], size[1], C] 或按比例缩放后的相应尺寸（其中 \"...\" 表示与输入相同的前导维数）。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "59abda69",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "原始图片尺寸为：(300, 280)\n",
      "缩放后的图片尺寸为：(100, 100)\n",
      "保存图片至：../tmp/cat.1_resize.jpg\n"
     ]
    }
   ],
   "source": [
    "def resize_image(image,size=(100,100)):# 可以随意调整这个参数感受一下\n",
    "    print(f\"原始图片尺寸为：{image.size}\") # 输出原始图片尺寸\n",
    "    resize = transforms.Resize(size)\n",
    "    image_after_resize = resize(image)\n",
    "    image_after_resize.show() # 显示缩放后的图片\n",
    "    print(f\"缩放后的图片尺寸为：{image_after_resize.size}\") # 输出缩放后的图片尺寸\n",
    "    return image_after_resize\n",
    "image_after_resize = resize_image(image) # 缩放图片\n",
    "save_image(image_after_resize,'../tmp/cat.1_resize.jpg')# 保存缩放后的图片"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "368bc9bb",
   "metadata": {},
   "source": [
    "## 8 torchvision.transforms.Compose\n",
    "**功能：**\n",
    "\n",
    "将多个图像变换串联起来\n",
    "\n",
    "**参数：**\n",
    "| 参数名 | 类型 | 必需 | 默认值 | 描述 |\n",
    "| :--- | :--- | :--- | :--- | :--- |\n",
    "| **`transforms`** | sequence (list or tuple) | 是 | 无 | 一个由多个变换操作（如 transforms.RandomHorizontalFlip, transforms.ColorJitter 等）组成的序列（列表或元组）。RandomChoice 会从这个序列中随机选择一个变换来应用。 |\n",
    "\n",
    "**返回：**\n",
    "\n",
    "返回的是经过随机选中的那个变换处理后的图像。输出的具体类型（PIL Image 或 Tensor）取决于所选的变换对输入的处理结果。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "98f4b387",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "保存图片至：../tmp/cat.1_compose.jpg\n"
     ]
    }
   ],
   "source": [
    "# 将center_crop、colour_jilter用Compose组合在一起\n",
    "image_after_Compose = transforms.Compose([\n",
    "    transforms.CenterCrop((100,100)),\n",
    "    transforms.ColorJitter(brightness=0.5,contrast=0.5,saturation=0.5,hue=0.5)])\n",
    "image_after_compose = image_after_Compose(image)\n",
    "image_after_compose.show() # 显示Compose后的图片\n",
    "save_image(image_after_compose,'../tmp/cat.1_compose.jpg')# 保存Compose后的图片\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d5048729",
   "metadata": {},
   "source": [
    "## 9 torchvision.transforms.RandomChoice\n",
    "**功能：**\n",
    "\n",
    "一个包含多个变换（transforms）的列表中随机选择其中一个变换并应用于输入图像（与Compose不同）。\n",
    "\n",
    "**参数：**\n",
    "| 参数名 | 类型 | 必需 | 默认值 | 描述 |\n",
    "| :--- | :--- | :--- | :--- | :--- |\n",
    "| **`transforms`** | sequence (list or tuple) | 是 | 无 | 一个由多个变换操作（如 transforms.RandomHorizontalFlip, transforms.ColorJitter 等）组成的序列（列表或元组）。RandomChoice 会从这个序列中随机选择一个变换来应用。 |\n",
    "\n",
    "**返回：**\n",
    "\n",
    "返回的是经过随机选中的那个变换处理后的图像。输出的具体类型（PIL Image 或 Tensor）取决于所选的变换对输入的处理结果。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "93e7bb47",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "保存图片至：../tmp/cat.1_random_choice.jpg\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "'\\ntrain_transforms = transforms.Compose([\\n    transforms.Resize(256),\\n    transforms.RandomChoice(transform_list),  # 在这里随机选择一种增强\\n    transforms.ToTensor()\\n])\\n'"
      ]
     },
     "execution_count": 53,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 定义一个包含多种增强选项的列表\n",
    "transform_list = [\n",
    "    transforms.CenterCrop(size=100),    # 选项1: 将图片裁剪为100*100的图形\n",
    "    transforms.ColorJitter(brightness=0.5),     # 选项2: 随机调整亮度\n",
    "    transforms.RandomRotation(30),              # 选项3: 随机旋转 ±30 度\n",
    "    transforms.Grayscale(num_output_channels=3)           # 选项4: 100%概率转为灰度图\n",
    "]\n",
    "\n",
    "# 创建 RandomChoice 变换\n",
    "random_choice_transform = transforms.RandomChoice(transform_list)\n",
    "\n",
    "# 应用变换：每次调用都会从 transform_list 中随机选择一个变换应用\n",
    "augmented_image = random_choice_transform(image)\n",
    "\n",
    "augmented_image.show()# 显示增强后的图片\n",
    "save_image(augmented_image,'../tmp/cat.1_random_choice.jpg')# 保存增强后的图片\n",
    "# 通常 RandomChoice 会和其他变换一起放在 Compose 中使用\n",
    "# 但注意：在 Compose 中使用 RandomChoice 时，每次执行到这个点都会随机一次\n",
    "\n",
    "'''\n",
    "train_transforms = transforms.Compose([\n",
    "    transforms.Resize(256),\n",
    "    transforms.RandomChoice(transform_list),  # 在这里随机选择一种增强\n",
    "    transforms.ToTensor()\n",
    "])\n",
    "'''"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f69d8304",
   "metadata": {},
   "source": [
    "## 10 torchvision.transforms.RandomOrder\n",
    "\n",
    "**功能：**\n",
    "\n",
    "将多个变换按随机顺序应用于输入图像，用于数据增强（与Compose不同）。\n",
    "\n",
    "**参数：**\n",
    "| 参数名 | 类型 | 必需 | 默认值 | 描述 |\n",
    "| :--- | :--- | :--- | :--- | :--- |\n",
    "| **`transforms`** | sequence (list or tuple) | 是 | 无 | 一个由多个变换操作（如 transforms.RandomHorizontalFlip, transforms.ColorJitter 等）组成的序列（列表或元组）。RandomChoice 会从这个序列中随机选择一个变换来应用。 |\n",
    "\n",
    "**返回：**\n",
    "\n",
    "返回的是经过随机选中的那个变换处理后的图像。输出的具体类型（PIL Image 或 Tensor）取决于所选的变换对输入的处理结果。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6edaf82e",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "保存图片至：../tmp/cat.1_random_order_1.jpg\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "'\\ntrain_transforms = transforms.Compose([\\n    transforms.Resize(256),\\n    transforms.CenterCrop(224),\\n    transforms.RandomOrder(transform_list),  # 在这里随机打乱变换顺序\\n    transforms.ToTensor(),\\n    transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])\\n])\\n'"
      ]
     },
     "execution_count": 54,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 定义一个包含多种增强变换的列表\n",
    "transform_list = [\n",
    "    transforms.CenterCrop(size=100),    # 选项1: 将图片裁剪为100*100的图形\n",
    "    transforms.ColorJitter(brightness=0.5),     # 选项2: 随机调整亮度\n",
    "    transforms.RandomRotation(30),              # 选项3: 随机旋转 ±30 度\n",
    "    transforms.Grayscale(num_output_channels=3)           # 选项4: 100%概率转为灰度图\n",
    "]\n",
    "\n",
    "# 创建 RandomOrder 变换\n",
    "random_order_transform = transforms.RandomOrder(transform_list)\n",
    "\n",
    "# 应用变换：每次调用都会随机打乱 transform_list 中变换的顺序后应用\n",
    "augmented_image_1 = random_order_transform(image)\n",
    "augmented_image_1.show()# 显示增强后的图片\n",
    "save_image(augmented_image_1,'../tmp/cat.1_random_order_1.jpg')# 保存增强后的图片\n",
    "\n",
    "# 通常 RandomOrder 会和其他变换一起放在 Compose 中使用\n",
    "# 注意：在 Compose 中使用 RandomOrder 时，每次执行到这个点都会进行一次随机排序\n",
    "'''\n",
    "train_transforms = transforms.Compose([\n",
    "    transforms.Resize(256),\n",
    "    transforms.CenterCrop(224),\n",
    "    transforms.RandomOrder(transform_list),  # 在这里随机打乱变换顺序\n",
    "    transforms.ToTensor(),\n",
    "    transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])\n",
    "])\n",
    "'''"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d712d591",
   "metadata": {},
   "source": [
    "## 11 torchvision.transforms.LinearTransformation(transformation_matrix, mean_vector)（了解）\n",
    "\n",
    "**功能：**\n",
    "\n",
    "在深度学习中，它通过对图像数据应用一个预先计算好的线性变换矩阵，使处理后的图像数据满足均值为零、各维度间无关且方差为1，从而加速模型收敛。另外这种做法说从PCA模型推导出来的（简单理解：先去中心化，再去相关）。\n",
    "\n",
    "**参数：**\n",
    "\n",
    "| 参数名 | 类型 | 必需 | 默认值 | 描述 |\n",
    "| :--- | :--- | :--- | :--- | :--- |\n",
    "| **`transformation_matrix`** | `torch.Tensor` | 是 | 无 | 变换矩阵，形状通常为 (num_features, num_features)。它定义了要对（展平后的）数据应用的主要线性变换，例如一个由PCA计算得到的白化矩阵。 |\n",
    "| **`mean_vector`** | `torch.Tensor` | 是 | 无 | 均值向量，形状为 (num_features,)。在应用 transformation_matrix 定义的变换之前，会先从（展平后的）输入数据中减去此均值向量。 |\n",
    "\n",
    "**返回：**\n",
    "输出张量的形状与输入张量的形状完全相同，torch Tensor"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "da826c0f",
   "metadata": {},
   "source": [
    "## 12 torchvision.transforms.Normalize\n",
    "**功能：**\n",
    "对张量形式的图像数据进行标准化（归一化）处理。在深度学习中，达到了三个目的：\n",
    "\n",
    "* 数据标准化：通过减去均值并除以标准差，将输入数据转换为均值为0、标准差为1的标准正态分布，使模型训练更加稳定和高效。\n",
    "\n",
    "* 加速收敛：标准化后的数据通常处于相似的数值范围内，有助于优化算法（如梯度下降）更快地收敛，减少训练时间。\n",
    "\n",
    "* 提升训练稳定性：防止因输入数据尺度差异过大导致的梯度爆炸或消失问题，提高模型训练的稳定性和可重复性。\n",
    "\n",
    "**参数：**\n",
    "| 参数名 | 类型 | 必需 | 默认值 | 描述 |\n",
    "| :--- | :--- | :--- | :--- | :--- |\n",
    "| **`mean`** | sequence | 是 | 无 | 各通道的均值。<br>- 对于单通道图像，提供一个数值。<br>- 对于多通道图像（如RGB），提供一个序列，包含每个通道的均值，例如 (mean_R, mean_G, mean_B)。 |\n",
    "| **`std`** | sequence | 是 | 无 | 各通道的标准差。<br>- 对于单通道图像，提供一个数值。<br>- 对于多通道图像（如RGB），提供一个序列，包含每个通道的标准差，例如 (std_R, std_G, std_B)。 |\n",
    "| **`inplace`** | bool | 否 | False | 是否原地操作。<br>- 若为 False：创建新的张量返回（默认）。<br>- 若为 True：直接在原张量上修改，不分配新内存。 |\n",
    "\n",
    "**返回：**\n",
    "Normalize 的返回值是标准化后的图像张量。其输入必须为 torch Tensor，返回的也是 torch Tensor。输出张量的形状与输入张量的形状完全相同，但数值已经过标准化处理：output = (input - mean) / std。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "bc3891c6",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "归一化后的图片数据类型为：<class 'torch.Tensor'>\n",
      "归一化后的图片数据尺寸为：torch.Size([3, 280, 300])\n",
      "保存图片至：../tmp/cat.1_normalize.jpg\n"
     ]
    }
   ],
   "source": [
    "def image_normalize(image,mean, std):\n",
    "    compose_transforms = transforms.Compose([\n",
    "        transforms.ToTensor(),\n",
    "        transforms.Normalize(mean=mean, std=std)\n",
    "    ])\n",
    "    image_after_normalize = compose_transforms(image)\n",
    "    img_save = transforms.ToPILImage()(image_after_normalize) # 将归一化后的图片转换为PIL格式以便保存\n",
    "\n",
    "    print(f\"归一化后的图片数据类型为：{type(image_after_normalize)}\") # 输出归一化后的图片数据类型\n",
    "    print(f\"归一化后的图片数据尺寸为：{image_after_normalize.size()}\") # 输出归一化后的图片数据尺寸\n",
    "    save_image(img_save,'../tmp/cat.1_normalize.jpg')# 保存归一化后的图片\n",
    "    return img_save\n",
    "#假设图片的均值和标准差如下（当然，你也可以先计算出图像数据集的均值和标准差）\n",
    "mean = [0.485, 0.456, 0.406]    # 假设的均值\n",
    "std = [0.229, 0.224, 0.225]     #   假设的标准差\n",
    "image_after_normalize = image_normalize(image,mean,std) # 归一化图片        \n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "279052dd",
   "metadata": {},
   "source": [
    "## 13 torchvison.transforms.ToPILImage\n",
    "**功能：**\n",
    "\n",
    "将张量或numpy数组转换为PIL图像格式。在深度学习中，这个变换主要用于以下场景：\n",
    "\n",
    "* 数据可视化：将在PyTorch中处理后的张量转换回PIL图像格式，便于使用matplotlib等库进行可视化展示和调试。\n",
    "\n",
    "* 预处理流水线的起点/终点：作为数据处理流程的最后一个步骤，将张量结果转换回图像格式用于保存或显示。\n",
    "\n",
    "* 格式兼容性：当需要使用只接受PIL图像格式的其他图像处理库时，进行格式转换。\n",
    "\n",
    "**参数：**\n",
    "| 参数名 | 类型 | 必需 | 默认值 | 描述 |\n",
    "| :--- | :--- | :--- | :--- | :--- |\n",
    "| **`mode`** | str | 否 | None |输出图像的颜色模式。如果为 None：根据输入张量的通道数自动推断模式。可指定为 'RGB', 'L', 'RGBA', 'CMYK', 'YCbCr' 等PIL支持的模式。 |\n",
    "\t\t\t\t\n",
    "\n",
    "**返回：** ToPILImage 的返回值是转换后的PIL图像。其输入可以是 torch Tensor 或 numpy.ndarray，返回的是 PIL.Image.Image 对象。输出图像的尺寸和颜色模式由输入张量的形状和指定的mode参数决定。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e8e16a5b",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "随机生成的tensor数据类型为：<class 'torch.Tensor'>\n",
      "保存图片至：../tmp/random_tensor_to_pil.jpg\n"
     ]
    }
   ],
   "source": [
    "#随机生成一个tensor,随后转化为PIL图片\n",
    "random_tensor = torch.randn(3, 224, 224) # 生成一个随机张量，模拟图像数据\n",
    "print(f\"随机生成的tensor数据类型为：{type(random_tensor)}\") # 输出随机生成的tensor数据类型\n",
    "to_pil = transforms.ToPILImage() # 定义ToPILImage变换   \n",
    "image_from_tensor = to_pil(random_tensor) # 将tensor转换为PIL图片\n",
    "image_from_tensor.show() # 显示转换后的图片 \n",
    "save_image(image_from_tensor,'../tmp/random_tensor_to_pil.jpg')# 保存转换后的图片"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e3fbead5",
   "metadata": {},
   "source": [
    "## 14 torchvision.transforms.ToTensor\n",
    "\n",
    "**功能：**\n",
    "将PIL图像或numpy数组转换为PyTorch张量。在深度学习中，这个变换是数据预处理流程中最基础且关键的步骤，主要实现以下功能：\n",
    "\n",
    "* 格式转换：将PIL图像或numpy数组转换为PyTorch框架能够直接处理的张量格式，这是模型训练和推理的前提。\n",
    "\n",
    "* 数值规范化：自动将图像像素值从[0, 255]的整数范围转换为[0.0, 1.0]的浮点数范围，使数据更适合神经网络处理。\n",
    "\n",
    "* 维度调整：将图像的维度顺序从(H, W, C)（高度、宽度、通道）调整为PyTorch标准格式(C, H, W)（通道、高度、宽度），满足卷积神经网络的输入要求\n",
    "  \n",
    "**参数：**\n",
    "无\n",
    "\n",
    "**返回：**\n",
    "\n",
    "ToTensor 的返回值是转换后的图像张量。其输入可以是PIL Image或numpy.ndarray，返回的是torch.Tensor。输出张量的形状为(C, H, W)，其中C为通道数，H为高度，W为宽度，数值范围为[0.0, 1.0]。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d1391900",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "原始图片数据类型为：<class 'PIL.Image.Image'>\n",
      "原始图片数据的尺寸为：(300, 280)\n",
      "转换后的tensor数据类型为：<class 'torch.Tensor'>\n",
      "转换后的tensor数据尺寸为：torch.Size([3, 280, 300])\n",
      "转化为tensor后的数据为： tensor([[[0.1529, 0.1529, 0.1569,  ..., 0.8118, 0.7922, 0.7882],\n",
      "         [0.1569, 0.1569, 0.1569,  ..., 0.7961, 0.7804, 0.7725],\n",
      "         [0.1569, 0.1569, 0.1569,  ..., 0.7804, 0.7804, 0.7804],\n",
      "         ...,\n",
      "         [0.1255, 0.1176, 0.1020,  ..., 0.1412, 0.1608, 0.2235],\n",
      "         [0.1255, 0.1176, 0.1020,  ..., 0.1647, 0.1725, 0.2157],\n",
      "         [0.1255, 0.1137, 0.0980,  ..., 0.2000, 0.1647, 0.1569]],\n",
      "\n",
      "        [[0.1725, 0.1725, 0.1725,  ..., 0.8000, 0.7804, 0.7765],\n",
      "         [0.1765, 0.1765, 0.1725,  ..., 0.7843, 0.7686, 0.7608],\n",
      "         [0.1765, 0.1765, 0.1725,  ..., 0.7647, 0.7608, 0.7608],\n",
      "         ...,\n",
      "         [0.1176, 0.1098, 0.0941,  ..., 0.0902, 0.1176, 0.1804],\n",
      "         [0.1176, 0.1098, 0.0941,  ..., 0.1137, 0.1333, 0.1765],\n",
      "         [0.1176, 0.1059, 0.0902,  ..., 0.1490, 0.1255, 0.1176]],\n",
      "\n",
      "        [[0.1569, 0.1569, 0.1686,  ..., 0.6784, 0.6510, 0.6392],\n",
      "         [0.1608, 0.1608, 0.1686,  ..., 0.6627, 0.6392, 0.6235],\n",
      "         [0.1608, 0.1608, 0.1686,  ..., 0.6510, 0.6431, 0.6353],\n",
      "         ...,\n",
      "         [0.1216, 0.1137, 0.0980,  ..., 0.0667, 0.0941, 0.1569],\n",
      "         [0.1216, 0.1137, 0.0980,  ..., 0.0824, 0.0980, 0.1412],\n",
      "         [0.1216, 0.1098, 0.0941,  ..., 0.1176, 0.0902, 0.0824]]])\n"
     ]
    }
   ],
   "source": [
    "image_to_tensor = transforms.ToTensor()(image) # 将PIL图片转换为tensor\n",
    "print(f\"原始图片数据类型为：{type(image)}\") # 输出原始图片数据类型\n",
    "print(f\"原始图片数据的尺寸为：{image.size}\") # 输出原始图片数据的尺寸\n",
    "print(f\"转换后的tensor数据类型为：{type(image_to_tensor)}\") # 输出转换后的tensor数据类型\n",
    "print(f\"转换后的tensor数据尺寸为：{image_to_tensor.size()}\") # 输出转换后的tensor数据尺寸  \n",
    "print(f\"转化为tensor后的数据为：\",image_to_tensor) # 输出tensor数据的值范围\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "da9528ff",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "py38_pytorch_cpu",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.20"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
